#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "raylib.h"
#include "pub_lib.h"
#include "FreeImage.h"
#include "image.h"
#include "dpi_algo.h"

//#define DEBUG

const int screenWidth = 1024;
const int screenHeight = 768;

cpu_image effect_data;
run_state gstate = STATE_IDLE;

float *ghue = NULL;
float *gsaturation = NULL;
float *gintensity = NULL;

FIBITMAP* gori_dib = NULL;
FIBITMAP* gchg_dib = NULL;

char* hint_right = NULL;
char hint_left[128] = "image size";

char g_select_str_default[] = "press 'enter' to confirm, 'z' to undo";
char g_select_str[128];
#define set_select_str_default() strcpy(g_select_str, g_select_str_default)


char smooth_image[] = "Fig0637(a)(caster_stand_original).tif";
char segment_image[] = "Fig0642(a)(jupiter_moon_original).tif";

#define MAX_CMD_NUM 5
const char command_str_en[MAX_CMD_NUM][MAX_HINT_STRING_LEN] =
{
	//6.5 Color Transformations
	"1. Histogram equalization", //6.5.5, example 6.11

	//6.6 smoothing and sharpening
	"2. smoothing",  //6.6.1, example 6.12
	"3. sharpening", //6.6.2, example 6.13

	//6.7 Image Segmentation
	"4. Segmentation in RGB", //6.7.2, example 6.15
	"5. Edge Detection" //6.7.3, example 6.16
};

const char command_str_cn[MAX_CMD_NUM][MAX_HINT_STRING_LEN] =
{
	"1. 直方图均衡",
	"2. 图像平滑",
	"3. 图像锐化",
	"4. 图像分割",
	"5. 边缘检测"
};

static void draw_intensity_diff(float* a, float* b, int pos);

void init_effect_data(void)
{
	memset(&effect_data, 0, sizeof(cpu_image));
	effect_data.win_width = screenWidth;
	effect_data.win_height = screenHeight;
	effect_data.fb_pixels = effect_data.win_width * effect_data.win_height;
	effect_data.startx = 10;
	effect_data.starty = 30;
	effect_data.left_max = get_max_pixel(screenWidth, 0.3f);
	effect_data.right_max = get_max_pixel(screenWidth, 0.7f);
	effect_data.right_image_max = effect_data.right_max;;

	effect_data.split_line = effect_data.startx + effect_data.left_max + 5;
	effect_data.lx = effect_data.startx;
	effect_data.rx = effect_data.split_line + 5;
}

void init_fb(void)
{
	effect_data.frame_buf = calloc(effect_data.fb_pixels, sizeof(Color));
}

void extra_init(void)
{
	int w, h, size, fsize;
	FIBITMAP* dib = effect_data.right;
	w = effect_data.rw;
	h = effect_data.rh;
	size = w * h;
	fsize = size * sizeof(float);

	ghue = malloc(fsize);
	gsaturation = malloc(fsize);
	gintensity = malloc(fsize);
	//RGB to HSI
	rgb2hsi_img(dib, ghue, gsaturation, gintensity);

	gori_dib = FreeImage_ConvertTo8Bits(dib);
	gchg_dib = copy_img(gori_dib);
}

void extra_destroy(void)
{
	//clean data
	if(gori_dib)
		close_img(gori_dib);
	if(gchg_dib)
		close_img(gchg_dib);

	if(ghue)
		free(ghue);
	if(gsaturation)
		free(gsaturation);
	if(gintensity)
		free(gintensity);
}


/*
 * 打开图片，读入图片数据
 * 原始图片信息:  ori
 * 左边显示的小图片: left
 * 右边显示的大图片: right
 * 右边显示的大图片的更改后的数据: rchg
 * 右边显示的大图片，中间计算的结果：rchg_data
 * */
int init_effect(char* path, int right_window_num)
{
	effect_data.right_window_num = right_window_num;
	const char* ext = GetFileExtension(path);

	FIBITMAP* ori;
	ori = open_img(path);
	if (!ori)
		return -1;
	if (!strcmp(ext, ".tif")) {
		read_img(ori, &effect_data);
		unsigned int bpp = FreeImage_GetBPP(ori);
		if (bpp == 24) {
			effect_data.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8;
		}
	}
	effect_data.show_histo = 0;
	effect_data.order = 0;
	effect_data.ori = (void*)ori;
	effect_data.left = (void*)ori;
	effect_data.right = (void*)ori;
	//check if need scale of left image
	int max = effect_data.left_max; //最长或最宽不超过360
	int neww = max, newh = max;
		if ( effect_data.image_width > max || effect_data.image_height > max) {
		cal_scaled_val(max, effect_data.image_width, effect_data.image_height,\
				&neww, &newh);
		effect_data.left = FreeImage_Rescale(ori, neww, newh, FILTER_BILINEAR);
	}

	//check if need scale of right image
	if(right_window_num == 1)
		effect_data.right_image_max = get_max_pixel(screenWidth, 0.7f);
	else
		effect_data.right_image_max = split_right_window(effect_data.win_width, 0.7f);

	max = effect_data.right_image_max;
	neww = effect_data.image_width;
	newh = effect_data.image_height;
	if (effect_data.image_width > max || effect_data.image_height > max) {
		cal_scaled_val(max, effect_data.image_width, effect_data.image_height, \
			&neww, &newh);
		effect_data.right = FreeImage_Rescale(ori, neww, newh, FILTER_BILINEAR);
	}

	sprintf(hint_left, "ori:%dx%d  chg:%dx%d",
		effect_data.image_width, effect_data.image_height, neww, newh);

	effect_data.rchg = FreeImage_Copy(effect_data.right, 0, 0, neww, newh);
	effect_data.pri  = FreeImage_Copy(effect_data.right, 0, 0, neww, newh);

	//int data[rw][rh]
	effect_data.rw = neww;
	effect_data.rh = newh;
	effect_data.rchg_data = calloc(neww * newh, sizeof(float));
	effect_data.rchg_image = calloc(neww * newh, 1);
	extra_init();

	effect_data.image_size = GetPixelDataSize(effect_data.image_width,\
		effect_data.image_height, effect_data.format);
	effect_data.pix_len = get_pixel_bytes(effect_data.image_width,\
		effect_data.image_height, effect_data.image_size);

	return 0;
}

void unload_image(void)
{
	if(effect_data.left != effect_data.ori)
		close_img(effect_data.left);
	if(effect_data.right != effect_data.ori)
		close_img(effect_data.right);
	if(effect_data.ori)
		close_img(effect_data.ori);
	if(effect_data.rchg)
		close_img(effect_data.rchg);
	if(effect_data.pri)
		close_img(effect_data.pri);

	if(effect_data.rchg_data)
		free(effect_data.rchg_data);
	if(effect_data.rchg_image)
		free(effect_data.rchg_image);
	extra_destroy();
}

void destroy_effect(void)
{
	free(effect_data.frame_buf);
	unload_image();
}

void switch_next_image(char* filename, int right_window_num)
{
	int ret;
	char path[256];

	clear_fb(&effect_data, RAYWHITE);
	unload_image();
	get_full_path(IMAGE_PATH, filename, path);
	ret = init_effect(path, right_window_num);
	if (ret)
		return;
	show_image(effect_data.left, &effect_data,\
		effect_data.startx, effect_data.starty);
}

void handle_key_down(void)
{
	
}

void handle_key_up(void)
{
}

//example 6.11
static void smooth_color(void)
{
	//1. figure 6.37c, histogram equalization for brightness
	fill_image_data_float(gori_dib, gintensity);
	get_histogram(gori_dib, effect_data.ori_histo);
	image_histogram_equa(gori_dib, gori_dib, NULL);
	get_histogram(gori_dib, effect_data.chg_histo);

	init_image_data_normalized(gori_dib, gintensity); //read changed data
	hsi2rgb_img(effect_data.rchg, ghue, gsaturation, gintensity);
	draw_right_image(effect_data.rchg, 1);

	//2. figure 6.37d, histogram equalization for saturation
	int i, size;
	size = effect_data.rw * effect_data.rh;
	//add saturation
	for(i = 0; i < size; i++) {
		gsaturation[i] *= 1.2;
		if(gsaturation[i] > 1)
			gsaturation[i] = 1;
	}
	//used changed gsaturation and gintensity to update image
	hsi2rgb_img(effect_data.rchg, ghue, gsaturation, gintensity);
	draw_right_image(effect_data.rchg, 2);
}

static void color_rgb_avg(FIBITMAP* ori, FIBITMAP* chg)
{
	int dim = 5; //5 * 5 filter
	int i;
	for(i = 0; i < 3; i++) {
		fill_image_data_color(ori, gori_dib, i); //fill certain color
		image_filter_avg(gori_dib, gchg_dib, dim); //filter
		fill_color_image_gray(chg, gchg_dib, i); //apply
	}
}

static void draw_intensity_diff(float* a, float* b, int pos)
{
	BYTE* image_data = effect_data.rchg_image;

	//Intensity Difference
	int i, size;
	size = effect_data.rw * effect_data.rh;
	for(i = 0; i < size; i++)
		a[i] = a[i] - b[i];
	scale_float_data(a, image_data, size);
	draw_right_data(image_data, pos);
}

static void color_image_avg(void)
{
	FIBITMAP* dib = effect_data.right;
	BYTE* image_data = effect_data.rchg_image;

	if(effect_data.order == 0) {
		gstate = STATE_WAIT_NEXT;
		hint_right = "RGB component: press space continue";
		effect_data.order++;

		init_image_datab_color(dib, image_data, 0); //R
		draw_right_data(image_data, 1);
		init_image_datab_color(dib, image_data, 1); //G
		draw_right_data(image_data, 2);
		init_image_datab_color(dib, image_data, 2); //B
		draw_right_data(image_data, 3);
	} else if(effect_data.order == 1) {
		hint_right = "HSI component: press space continue";
		effect_data.order++;
		clear_fb_right(&effect_data, RAYWHITE);
		rgb2hsi_img(dib, ghue, gsaturation, gintensity);

		int w, h;
		w = effect_data.rw;
		h = effect_data.rh;
		convert_hue_data(ghue, w, h, image_data);
		draw_right_data(image_data, 1);
		convert_saturat_data(gsaturation, w, h, image_data);
		draw_right_data(image_data, 2);
		convert_intense_data(gintensity, w, h, image_data);
		draw_right_data(image_data, 3);
	} else {
		hint_right = "neightborhood averaging";
		effect_data.order++;
		gstate = STATE_FINISH;
		clear_fb_right(&effect_data, RAYWHITE);

		//1. RGB avg filter
		color_rgb_avg(effect_data.right, effect_data.rchg);
		draw_right_image(effect_data.rchg, 1);

		//2. HSI avg filter
		float *chg_gintensity = effect_data.rchg_data;

		fill_image_data_float(gori_dib, gintensity); //fill dib
		image_filter_avg(gori_dib, gchg_dib, 5); //filter
		init_image_data_normalized(gchg_dib, chg_gintensity); //write to gintensity
		hsi2rgb_img(effect_data.pri, ghue, gsaturation, chg_gintensity); //applay
		draw_right_image(effect_data.pri, 2);

		//3. RGB difference
		color_image_sub(effect_data.rchg, effect_data.pri, effect_data.rchg);
		draw_right_image(effect_data.rchg, 3);

		//4. Intensity Difference
		draw_intensity_diff(gintensity, chg_gintensity, 4);
	}
}

static void color_image_gradient_scale(float* scaled_data)
{
	int w, h, size;
	FIBITMAP* dib = effect_data.ori;
	w = FreeImage_GetWidth(dib);
	h = FreeImage_GetHeight(dib);
	size = w * h * sizeof(float);
	float* data = malloc(size);
	BYTE* image = malloc(w * h);

	//calc gradient
	color_image_gradient_merge(dib, data);
	scale_float_data(data, image, w * h);

	//resize to show
	FIBITMAP* chg = FreeImage_ConvertTo8Bits(dib);
	fill_image_data(chg, image);
	FIBITMAP* scaled = FreeImage_Rescale(chg, effect_data.rw, effect_data.rh, FILTER_BILINEAR);
	init_image_data(scaled, scaled_data);

	close_img(scaled);
	close_img(chg);
	free(image);
	free(data);
}

//example 6.16 Edge dection in vector space
static void color_image_edge(void)
{
	FIBITMAP* dib = effect_data.right;
	BYTE* image_data = effect_data.rchg_image;
	int size = effect_data.rw * effect_data.rh;

	if(effect_data.order == 0) {
		gstate = STATE_WAIT_NEXT;
		hint_right = "Component gradient: press space continue";
		effect_data.order++;

		init_image_datab_color(dib, image_data, 0); //R
		draw_right_data(image_data, 1);
		int i;
		//calc R G B gradient and draw
		for(i = 0; i < 3; i++) {
			fill_image_data_color(dib, gori_dib, i); //fill certain color
			image_filter_grad_sobel(gori_dib, gchg_dib); //filter
			init_image_datab(gchg_dib, image_data); //write to image_data
			draw_right_data(image_data, i + 1);
		}
	} else {
		hint_right = "Gradients computed in RGB";
		effect_data.order++;
		gstate = STATE_FINISH;
		clear_fb_right(&effect_data, RAYWHITE);

		//6.46b, Gradient computed in RGB color vector space
		//color_image_gradient(dib, gintensity);
		color_image_gradient_scale(gintensity);
		scale_float_data(gintensity, image_data, size);
		draw_right_data(image_data, 1);

		//show 6.46c
		color_image_gradient_merge(dib, gsaturation);
		scale_float_data(gsaturation, image_data, size);
		draw_right_data(image_data, 2);

		//show difference
		//6.46b - 6.46c
		draw_intensity_diff(gintensity, gsaturation, 3);
	}
}

void handle_key_space(void)
{
	if(gstate != STATE_WAIT_NEXT)
		return;

	switch(effect_data.cmd) {
		case 2:
			color_image_avg();
			break;
		case 5:
			color_image_edge();
			break;
		default:
			break;
	}
}

static void color_rgb_sharpen(FIBITMAP* ori, FIBITMAP* chg)
{
	int i;
	for(i = 0; i < 3; i++) {
		fill_image_data_color(ori, gori_dib, i); //fill certain color
		image_filter_lapaction_final(gori_dib, gchg_dib, 3, NULL);
		fill_color_image_gray(chg, gchg_dib, i); //apply
	}
}

//example 6.13
static void color_image_sharpen(void)
{
	float* chg_gintensity = effect_data.rchg_data;

	//RGB sharpen
	color_rgb_sharpen(effect_data.right, effect_data.rchg);
	draw_right_image(effect_data.rchg, 1);

	//HSI sharpen, only change gintensity
	fill_image_data_float(gori_dib, gintensity); //fill dib
	image_filter_lapaction_final(gori_dib, gchg_dib, 3, NULL); //filter
	init_image_data_normalized(gchg_dib, chg_gintensity); //write to gintensity
	hsi2rgb_img(effect_data.pri, ghue, gsaturation, chg_gintensity); //applay
	draw_right_image(effect_data.pri, 2);

	//difference
	draw_intensity_diff(gintensity, chg_gintensity, 3);
}

//example 6.15
//measured by define DEBUG
//(376, 271) (417, 348)
const Rectangle measured_rect = {376, 271, 417, 348};
Rectangle grect;
int g_select_num = 0;

Rectangle draw_rect;
static void convert_coord(void)
{
	memcpy(&grect, &measured_rect, sizeof(Rectangle));
	grect.x = grect.x - effect_data.rx;
	grect.y = grect.y - effect_data.starty;
	grect.width = grect.width - effect_data.rx;
	grect.height = grect.height - effect_data.starty;
	printf("Select rectangle from image(%d, %d) (%d, %d) \n",
		(int)grect.x, (int)grect.y, (int)grect.width, (int)grect.height);

	int lw = FreeImage_GetWidth(effect_data.left);
	float scale = (float)lw / effect_data.rw;
	draw_rect.x = grect.x * scale + effect_data.startx;
	draw_rect.y = grect.y * scale + effect_data.starty;
	int w, h;
	w = grect.width - grect.x;
	h = grect.height - grect.y;
	draw_rect.width = w * scale;
	draw_rect.height = h * scale;
}

static void color_image_rect_avg(FIBITMAP* dib, Rectangle* rect, Vector3* mean)
{
	int x, y, sx, sy, ex, ey;
	sx = rect->x;
	sy = rect->y;
	ex = rect->width;
	ey = rect->height;

	RGBQUAD rgb;
	int num = 0;
	Vector3 sum = {0, 0, 0};
	int h = effect_data.rh;
	for(y = sy; y <= ey; y++) {
		for(x = sx; x <= ex; x++) {
			FreeImage_GetPixelColor(dib, x, h - y, &rgb);
			sum.x += rgb.rgbRed;
			sum.y += rgb.rgbGreen;
			sum.z += rgb.rgbBlue;
			num++;
		}
	}
	mean->x = sum.x / num;
	mean->y = sum.y / num;
	mean->z = sum.z / num;
}

static void color_image_rect_variance(FIBITMAP* dib, Rectangle* rect, Vector3* mean, Vector3* variance)
{
	int x, y, sx, sy, ex, ey;
	sx = rect->x;
	sy = rect->y;
	ex = rect->width;
	ey = rect->height;

	RGBQUAD rgb;
	int num = 0;
	Vector3 sum = {0, 0, 0};
	int h = effect_data.rh;
	for(y = sy; y <= ey; y++) {
		for(x = sx; x <= ex; x++) {
			FreeImage_GetPixelColor(dib, x, h - y, &rgb);
			sum.x += (rgb.rgbRed - mean->x) * (rgb.rgbRed - mean->x);
			sum.y += (rgb.rgbGreen - mean->y) * (rgb.rgbGreen - mean->y);
			sum.z += (rgb.rgbBlue - mean->z) * (rgb.rgbBlue - mean->z);
			num++;
		}
	}

	variance->x = sqrtf(sum.x / num);
	variance->y = sqrtf(sum.y / num);
	variance->z = sqrtf(sum.z / num);
}

static int cir_component(BYTE color, float mean, float variance, float k)
{
	int ret = 0;
	float min, max;

	min = mean - k * variance;
	max = mean + k * variance;
	if(color > min && color < max)
		ret = 1;
	return ret;
}

static int color_in_range(RGBQUAD* rgb, Vector3* mean, Vector3* variance, float k)
{
	int ret = 0;
	int r = 0, g = 0, b = 0;

	r = cir_component(rgb->rgbRed, mean->x, variance->x, k);
	g = cir_component(rgb->rgbGreen, mean->y, variance->y, k);
	b = cir_component(rgb->rgbBlue, mean->z, variance->z, k);
	if(r && g && b)
		ret = 1;
	return ret;
}

static void color_image_segment(void)
{
	convert_coord();
	FIBITMAP* dib = effect_data.right;

	//calc mean vector a
	RGBQUAD rgb;
	Vector3 a_mean, variance;
	color_image_rect_avg(dib, &grect, &a_mean);
	color_image_rect_variance(dib, &grect, &a_mean, &variance);

	int x, y, index;
	float k = 1.25;
	for(y = 0; y < effect_data.rh; y++) {
		for(x = 0; x < effect_data.rw; x++) {
			FreeImage_GetPixelColor(effect_data.right, x, y, &rgb);
			index = y * effect_data.rw + x;
			if(color_in_range(&rgb, &a_mean, &variance, k))
				effect_data.rchg_image[index] = 255;
			else
				effect_data.rchg_image[index] = 0;
		}
	}

	draw_right_data(effect_data.rchg_image, 1);
}

void special_effect(int cmd)
{
	//repeate press same key
	if (effect_data.cmd == cmd)
		return;

	if(cmd <= MAX_CMD_NUM) {
		effect_data.cmd = cmd;
		hint_right = (char*)command_str_en[cmd - 1];
		SetWindowTitle(command_str_cn[cmd - 1]);
		printf("%s\n", command_str_cn[cmd - 1]);
	}

	switch (cmd) {
	case 1: //example 6.11
		switch_next_image(smooth_image, 4);
		smooth_color();
		break;
	case 2: //example 6.12
		switch_next_image("lena.tif", 4);
		color_image_avg();
		break;
	case 3: //example 6.13
		switch_next_image("lena.tif", 4);
		color_image_sharpen();
		break;
	case 4:
		switch_next_image(segment_image, 1);
#ifdef DEBUG
		draw_right_image(effect_data.right, 1);
		gstate = STATE_WAIT_NEXT;
		hint_right = "select a rectangle area";
#else
		color_image_segment();
#endif
		break;
	case 5:
		switch_next_image("lena.tif", 4);
		color_image_edge();
		break;
	case KEY_DOWN:
		handle_key_down();
		break;
	case KEY_UP:
		handle_key_up();
		break;
	case KEY_SPACE:
		handle_key_space();
		break;
	default:
		hint_right = "not support command";
		break;
	}//end switch
}

static void draw_titles(void)
{
	switch(effect_data.cmd) {
		case 1:
			draw_title("brightness equal", 1); //figure 6.37c
			draw_title("saturation rise", 2); //figure 6.37d
			draw_title("histogram original", 3);
			draw_right_histo(effect_data.ori_histo, 3);
			draw_title("histogram changed", 4);
			draw_right_histo(effect_data.chg_histo, 4);
			break;
		case 2: //example 6.12
			if(effect_data.order == 1) {
				draw_title("Red", 1);
				draw_title("Green", 2);
				draw_title("Blue", 3);
			} else if(effect_data.order == 2) {
				draw_title("Hue", 1);
				draw_title("Saturation", 2);
				draw_title("Intensity", 3);
			} else {
				draw_title("RGB smoothing", 1);
				draw_title("HSI smoothing", 2);
				draw_title("RGB Difference", 3);
				draw_title("Intensity Difference", 4);
			}
			break;
		case 3: //example 6.13
			draw_title("RGB sharpen", 1);
			draw_title("HSI sharpen", 2);
			draw_title("Difference", 3);
			break;
		case 4:
#ifdef DEBUG
			draw_title(g_select_str, 1);
#else
			draw_title("sgementation in RGB 6.44b", 1);
#endif
			break;
		case 5:
			if(effect_data.order == 1) {
				//figure 6.47
				draw_title("Red component", 1);
				draw_title("Green component",2 );
				draw_title("Blue component", 3);
			} else {
				//figure 6.46
				draw_title("Gradient in RGB, 6,46b", 1);
				draw_title("Added result, 6.46c", 2);
				draw_title("Difference, 6.46d", 3);
			}
			break;
		default:
			break;
	}
}

void interact_select(int key)
{
	Vector2 mouse;

	if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) {
		//select one apex of rectangle
		SetMouseCursor(MOUSE_CURSOR_CROSSHAIR);
		g_select_num = 1;
		mouse = GetMousePosition();
		grect.x = mouse.x;
		grect.y = mouse.y;
	} else if(IsMouseButtonDown(MOUSE_BUTTON_LEFT)) {
		mouse = GetMousePosition();
		grect.width = fabs(grect.x - mouse.x);
		grect.height = fabs(grect.y - mouse.y);
	} else if(IsMouseButtonReleased(MOUSE_BUTTON_LEFT)) {
		//confirm
		mouse = GetMousePosition();
		grect.width = fabs(grect.x - mouse.x);
		grect.height = fabs(grect.y - mouse.y);

		sprintf(g_select_str, "Select (%d, %d) (%d, %d) OK, 'z' to undo",
			(int)grect.x, (int)grect.y, (int)mouse.x, (int)mouse.y);
		SetMouseCursor(MOUSE_CURSOR_DEFAULT);
	} else if(key == KEY_Z) {
		//undo
		g_select_num = 0;
		grect.width = 0;
	}
}

void draw_mouse_select(void)
{
	if(effect_data.cmd != 4)
		return;

#ifdef DEBUG
	if(!g_select_num)
		return;
	if(grect.width < 5 || grect.height < 5)
		return;
	DrawRectangleLinesEx(grect, 3, BLUE);
#else
	DrawRectangleLinesEx(draw_rect, 1.5, BLUE);
#endif
}


int main(int argc, char** argv)
{
	// Initialization
	//--------------------------------------------------------------------------------------
	FreeImage_Initialise(TRUE);
	init_effect_data();
	init_fb();

	InitWindow(screenWidth, screenHeight, "demo");
	// NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required)
	SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
	//--------------------------------------------------------------------------------------

	int key = 0;
	int change = 0;
	Vector2 mouse = GetMousePosition();
	hint_right = "press number key";
	print_info_cn(command_str_cn, MAX_CMD_NUM);

	set_select_str_default();
	// Main game loop
	while (!WindowShouldClose())    // Detect window close button or ESC key
	{
		// Update
		key = GetKeyPressed();
		if (key >= '1' && key <= '9') {
			change = 1;
			key = key - '0';
		} else if(key == KEY_DOWN || key == KEY_UP){
			change = 1;
		}
		else if(key == KEY_SPACE) {
			change = 1;
		}
		else if (key == ESC_KEY) {
			break;
		}

#ifdef DEBUG
		interact_select(key);
#endif

		if (change) {
			change = 0;
			special_effect(key);
		}
		//----------------------------------------------------------------------------------
		//----------------------------------------------------------------------------------

		// Draw
		//----------------------------------------------------------------------------------
		BeginDrawing();
		ClearBackground(RAYWHITE);

		DrawText(hint_left, effect_data.startx, 5, 20, BLACK);
		if (hint_right) {
			DrawText(hint_right, effect_data.rx, 0, 20, BLACK);
		}
		show_info_en(command_str_en, MAX_CMD_NUM, effect_data.startx,\
			effect_data.left_max + effect_data.starty + 20);
		draw_image(effect_data.frame_buf, effect_data.win_width, effect_data.win_height);
		DrawLine(effect_data.split_line, effect_data.starty,\
			effect_data.split_line, GetScreenHeight(), GRAY);
		draw_titles();
		draw_mouse_select();

		mouse = GetMousePosition();
		DrawText(TextFormat("mouse(%d, %d)", (int)mouse.x, (int)mouse.y), \
			GetScreenWidth() - 200, 10, 20, BLACK);

		EndDrawing();
		//----------------------------------------------------------------------------------
	}

	// De-Initialization
	destroy_effect();
	FreeImage_DeInitialise();

	//--------------------------------------------------------------------------------------
	CloseWindow();        // Close window and OpenGL context
	//--------------------------------------------------------------------------------------

	return 0;
}


